﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace oms
{

    delegate void EventHandle(string msg, params object[] objs);

    /// <summary>
    /// 事件通知系统（**不支持多线程**）
    /// 1. 全局事件
    /// 2. 对象事件
    /// 
    /// RegisterGlobal("msg",handle)
    /// RemoveGlobal("msg",handle)
    /// TriggerGlobal("msg",params)
    /// 
    /// Register(trigger_obj,"msg",handle)
    /// Remove(trigger_obj,"msg",handle)
    /// RemoveByTriggerObj(trigger_obj)
    /// Trigger(trigger_obj,"msg",params)
    /// 
    /// </summary>
    class EventControl
    {
        /// <summary>
        /// 事件处理函数容器
        /// 
        /// 解决delegate做容器的2个问题。
        /// 1. 重复添加，丢弃处理【delegate是添加两次】
        /// 2. remove会立即生效，**不会再执行**【delegate在回调执行时remove是延后生效的】
        /// 
        /// PS: 连着Add、Remove相当于什么都没做。
        /// </summary>
        private class EventHandleContain
        {
            private bool _is_runing = false;
            private HashSet<EventHandle> _contain = new HashSet<EventHandle>();
            /// <summary>
            /// 延后处理，bool确定处理的类型，true:remove，false:add。
            /// </summary>
            private Dictionary<EventHandle, bool> _delay_ops = new Dictionary<EventHandle, bool>();
            public void Notify(string msg, params object[] objs)
            {
                _is_runing = true;
                foreach (EventHandle handle in _contain)
                {
                    if(_delay_ops.ContainsKey(handle) && _delay_ops[handle])
                    {
                        continue;
                    }
                    handle(msg, objs);
                }
                _is_runing = false;
                _DelayAddAndRemove();
            }

            private void _DelayAddAndRemove()
            {
                foreach(var item in _delay_ops)
                {
                    if(item.Value)
                    {
                        _contain.Remove(item.Key);
                    }
                    else
                    {
                        _contain.Add(item.Key);
                    }
                }
                _delay_ops.Clear();
            }

            public void Add(EventHandle handle)
            {
                if(_is_runing)
                {
                    _delay_ops[handle] = false;
                }
                else
                {
                    _contain.Add(handle);
                }
            }

            public void Remove(EventHandle handle)
            {
                if (_is_runing)
                {
                    _delay_ops[handle] = true;
                }
                else
                {
                    _contain.Remove(handle);
                }
            }
        }


        private Dictionary<object, Dictionary<string, EventHandleContain>> _object_contains = new Dictionary<object, Dictionary<string, EventHandleContain>>();
        public void Register(object trigger_obj, string msg, EventHandle handle)
        {
            Dictionary<string, EventHandleContain> dic2 = null;
            _object_contains.TryGetValue(trigger_obj, out dic2);
            if (dic2 == null)
            {
                dic2 = new Dictionary<string, EventHandleContain>();
                _object_contains.Add(trigger_obj, dic2);
            }
            if(dic2.ContainsKey(msg))
            {
                dic2[msg].Add(handle);
            }
            else
            {
                var contain = new EventHandleContain();
                contain.Add(handle);
                dic2.Add(msg, contain);
            }
        }

        public void Remove(object trigger_obj, string msg, EventHandle handle)
        {
            if(_object_contains.ContainsKey(trigger_obj))
            {
                var dic2 = _object_contains[trigger_obj];
                if (dic2 != null && dic2.ContainsKey(msg))
                {
                    dic2[msg].Remove(handle);                    
                }
            }
        }

        public void RemoveByTriggerObj(object trigger_obj)
        {
            _object_contains.Remove(trigger_obj);
        }

        public void Trigger(object trigger_obj, string msg, params object[] objs)
        {
            Dictionary<string, EventHandleContain> dic2 = null;
            _object_contains.TryGetValue(trigger_obj, out dic2);
            if(dic2 != null)
            {
                EventHandleContain contain = null;
                dic2.TryGetValue(msg, out contain);
                if (contain != null)
                {
                    contain.Notify(msg, objs);
                }
            }
        }


        private Dictionary<string, EventHandleContain> _global_contains = new Dictionary<string, EventHandleContain>();
        public void RegisterGlobal(string msg, EventHandle handle)
        {
            if(_global_contains.ContainsKey(msg))
            {
                _global_contains[msg].Add(handle);
            }
            else
            {
                var contain = new EventHandleContain();
                contain.Add(handle);
                _global_contains.Add(msg, contain);
            }
        }

        public void RemoveGlobal(string msg, EventHandle handle)
        {
            if (_global_contains.ContainsKey(msg))
            {
                _global_contains[msg].Remove(handle);
                // 就不检查容器为空了。
            }
        }

        public void TriggerGlobal(string msg, params object[] objs)
        {
            EventHandleContain contain = null;
            _global_contains.TryGetValue(msg, out contain);
            if(contain != null)
            {
                contain.Notify(msg, objs);
            }
        }
    }
}
